package client;

import common.Encrypt;
import common.TextMessage;

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;

/**
 * simple chat client
 */
public class Client implements Runnable {
    protected ObjectInputStream inputStream;
    protected ObjectOutputStream outputStream;
    protected Thread thread;
    protected History history;
    /**
     * listener-list for the observer pattern
     */
    private ArrayList listeners = new ArrayList();

    public Client(String host, int port) {
        try {
            System.out.println("Connecting to " + host + " (port " + port
                    + ")...");
            Socket s = new Socket(host, port);

            // created history
            history = new History();
            this.outputStream = new ObjectOutputStream((s.getOutputStream()));
            this.inputStream = new ObjectInputStream((s.getInputStream()));
            thread = new Thread(this);
            thread.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String args[]) throws IOException {
        Encrypt.setEncryptMode(Encrypt.SWAP_ENCRYPTION);
        if (args.length != 2)
            throw new RuntimeException("Syntax: ChatClient <host> <port>");

        Client client = new Client(args[0], Integer.parseInt(args[1]));
        new Gui("Chat " + args[0] + ":" + args[1], client);
    }

    /**
     * main method. waits for incoming messages.
     */
    public void run() {
        try {
            Thread thisthread = Thread.currentThread();
            while (thread == thisthread) {
                try {
                    Object msg = inputStream.readObject();
                    handleIncomingMessage(msg);
                } catch (EOFException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            thread = null;
            try {
                outputStream.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * decides what to do with incoming messages
     *
     * @param msg the message (Object) received from the sockets
     */
    protected void handleIncomingMessage(Object msg) {
        if (msg instanceof TextMessage) {
            if (!((TextMessage) msg).isSystem_Message()) {
                fireAddLine((Encrypt.encrypt((TextMessage) msg)).getContent() + "\n");
            } else {
                fireAddLine(((TextMessage) msg).getContent()  + "\n");
            }
            // log message
            history.writeMessage(Encrypt.encrypt((TextMessage) msg));
        }
    }

    public void send(String line) {
        send(new TextMessage(line));
    }

    public void send(TextMessage msg) {
        try {
            outputStream.writeObject(Encrypt.encrypt(msg));
            outputStream.flush();
        } catch (IOException ex) {
            ex.printStackTrace();
            this.stop();
        }
    }

    /**
     * addListner method for the observer pattern
     */
    public void addLineListener(ChatLineListener listener) {
        listeners.add(listener);
    }

    /**
     * removeListner method for the observer pattern
     */
    public void removeLineListener(ChatLineListener listner) {
        listeners.remove(listner);
    }

    /**
     * fire Listener method for the observer pattern
     */
    public void fireAddLine(String line) {
        for (Iterator iterator = listeners.iterator(); iterator.hasNext(); ) {
            ChatLineListener listener = (ChatLineListener) iterator.next();
            listener.newChatLine(line);
        }
    }

    public void stop() {
        thread = null;
    }

    public History getHistory() {
        return history;
    }
}
